# -*- coding: utf-8 -*-
"""
Created on Thu May 11 15:33:28 2023

@author: ZHOU
"""
import time
import random
from decimal import Decimal
import matplotlib.pyplot as plot
from datetime import datetime
import math

data_choice_1=[-5,-4,-4,-3,-3,-3,-2,-2,-2,-1,-1,-1,-1,0,0,0,0,1,1,1,1,2,2,2,3,3,3,4,4,5]

data_choice_2=[Decimal("-0.000009"),Decimal("-0.000006"),Decimal("-0.000006"),\
               Decimal("-0.000003"),Decimal("-0.000003"),Decimal("-0.000003"),\
               Decimal("-0.000001"),Decimal("0.000001"),Decimal("0"),\
               Decimal("0.000003"),Decimal("0.000003"),Decimal("0.000003"),\
               Decimal("0.000009"),Decimal("0.000006"),Decimal("0.000006")]

# 定义各种GPX文件标志
COROS_GPX_FLAG = '''<?xml version="1.0" encoding="UTF-8"?>
<gpx xmlns="http://www.topografix.com/GPX/1/1" xmlns:gpxdata="http://www.cluetrust.com/XML/GPXDATA/1/0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 https://www.topografix.com/GPX/1/1/gpx.xsd http://www.cluetrust.com/XML/GPXDATA/1/0 https://www.cluetrust.com/Schemas/gpxdata10.xsd" creator="COROS Wearables" version="1.1">
 <metadata>
  <link href="https://www.coros.com">
   <text>COROS</text>
  </link>'''

GARMIN_GPX_FLAG = '''<?xml version="1.0" encoding="UTF-8"?>
<gpx xmlns="http://www.topografix.com/GPX/1/1" xmlns:gpxx="http://www.garmin.com/xmlschemas/GpxExtensions/v3" xmlns:gpxtpx="http://www.garmin.com/xmlschemas/TrackPointExtension/v1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www.garmin.com/xmlschemas/GpxExtensionsv3.xsd http://www.garmin.com/xmlschemas/TrackPointExtension/v1 http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd" creator="Garmin Connect" version="1.1">
 <metadata>
  <link href="https://connect.garmin.com">
   <text>Garmin Connect</text>
  </link>'''

STRAVA_GPX_FLAG = '''<?xml version="1.0" encoding="UTF-8"?>
<gpx xmlns="http://www.topografix.com/GPX/1/1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd" creator="StravaGPX" version="1.1">
 <metadata>
  <link href="https://www.strava.com">
   <text>Strava</text>
  </link>'''

STANDARD_GPX_FLAG = '''<?xml version="1.0" encoding="UTF-8"?>
<gpx xmlns="http://www.topografix.com/GPX/1/1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd" creator="GPX Editor" version="1.1">
 <metadata>
  <link href="https://example.com">
   <text>GPX Editor</text>
  </link>'''

# GPX标志映射字典
GPX_FLAGS = {
    "COROS": COROS_GPX_FLAG,
    "Garmin": GARMIN_GPX_FLAG,
    "Strava": STRAVA_GPX_FLAG,
    "Standard": STANDARD_GPX_FLAG
}

'''gpx文件格式说明
<?xml version="1.0" encoding="UTF-8"?> # 版本
<gpx xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www.garmin.com/xmlschemas/GpxExtensionsv3.xsd http://www.garmin.com/xmlschemas/TrackPointExtension/v1 http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd" creator="StravaGPX" version="1.1" xmlns="http://www.topografix.com/GPX/1/1" xmlns:gpxtpx="http://www.garmin.com/xmlschemas/TrackPointExtension/v1" xmlns:gpxx="http://www.garmin.com/xmlschemas/GpxExtensions/v3">
 <metadata>
  <time>2025-08-30T10:55:24Z</time> # 运动时间
 </metadata>
 <trk>
  <name>傍晚骑行</name> # 名称
  <type>cycling</type> # 运动类型
  <trkseg> # 开始轨迹记录

    # trkpt属性 数据点记录
    # 每次记录一个点 必须要有坐标信息 ele是海拔 如果没有time 则只能做路书 而不能做运动记录
    # extensions属性中为额外信息 如心率等
   <trkpt lat="30.5771990" lon="114.3662630">
    <ele>30.7</ele> # 海拔
    <time>2025-08-30T10:55:24Z</time> # 时间
    <extensions>
     <power>245</power>
     <cadence>80</cadence>
     <heartrate>120</heartrate>
    </extensions>
   </trkpt> # 结束记录一个点

    # 第二个点记录
   <trkpt lat="30.5771990" lon="114.3662650">
    <ele>30.7</ele>
    <time>2025-08-30T10:55:24Z</time>
    <extensions>
     <power>245</power>
     <cadence>80</cadence>
     <heartrate>120</heartrate>
    </extensions>
   </trkpt>
   
   #...  # 其他点记录

   # 最后一个点记录
   <trkpt lat="30.5860920" lon="114.3705440">
    <ele>27.4</ele>
    <time>2025-08-30T12:05:35Z</time>
    <extensions>
     <power>245</power>
     <cadence>80</cadence>
     <heartrate>120</heartrate>
    </extensions>
   </trkpt>

  </trkseg>  # 结束轨迹记录
 </trk>
</gpx>

'''

'''data keywords gpx文件关键字extensions的数据类型说明
心率 "heartrate"
踏频 "cadence"
距离 "distance"
功率 "power"
速度 "speed"

基础记录方式：
<extensions>
     <power>245</power>
     <cadence>80</cadence>
     <distance>1000</distance>
     <heartrate>120</heartrate>
</extensions>
或多个extensions属性分开记录：
    <extensions><power>245</power></extensions>
    <extensions><cadence>80</cadence></extensions>
    <extensions><distance>1000</distance></extensions>
    <extensions><heartrate>120</heartrate></extensions>

也有其他的记录方式 如gpxtpx属性的多行记录表示：
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>96</gpxtpx:hr> # 心率
<gpxtpx:cad>80</gpxtpx:cad> # 踏频
</gpxtpx:TrackPointExtension>

但这种表示方法并不规范 是某些软件转换后的结果 建议使用第一种基础记录方式
若得到的gpx文件中包含gpxtpx属性的多行记录 则建议进行转换为基础记录方式 或先删除再进行添加

删除属性可以调用函数 clean_data() 传参时传入"extensions" 即将所有extensions删除
也可以只删除gpxtpx:TrackPointExtension或gpxtpx:hr等属性


# 注意 distance是累加数据
'''

keywords_list=["heartrate","cadence","power","distance","speed"]

"""
    将秒数转换为两位数字符串
    
    参数:
        st (int): 需要格式化的时间数值（0-59）
        
    返回:
        str: 两位数的字符串格式时间
"""
def trans_time(st):
    if st<10 and st>=0:
        st="0"+str(st)
    else:
        st=str(st)
    return str(st)

"""
    找出轨迹的极值坐标点
    
    参数:
        gpx (list): 包含GPX原始数据的字符串列表
        
    返回:
        list: 包含四个极值点的二维坐标列表，格式为:
            [[最北点lat, lon],
             [最南点lat, lon],
             [最东点lat, lon],
             [最西点lat, lon]]
"""
def find_pole(gpx):
    south=-90
    north=90
    west=-180
    east=180
    location=[[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0]]
    for i in range(len(gpx)):
        if gpx[i].count('<trkpt'):
            s_list=gpx[i].split('"')
            lat=Decimal(s_list[1])
            lon=Decimal(s_list[3])
            if lat>south:  #最北
                south=lat
                location[0]=[lat,lon]
            if lat<north:  #最南
                north=lat
                location[1]=[lat,lon]
            if lon>west:  #最东
                west=lon
                location[2]=[lat,lon]
            if lon<east:  #最西
                east=lon
                location[3]=[lat,lon]
    return location

"""
    获取GPX轨迹的起点和终点坐标
    
    参数:
        gpx (list): 原始GPX读取后的数据列表，每个元素为字符串行
        
    返回:
        list: 包含起点和终点的二维坐标列表，格式为:
            [[起点lat, lon],
             [终点lat, lon]]
    
    实现说明:
        1. 遍历GPX数据，记录第一个<trkpt>节点作为起点
        2. 持续更新最后一个<trkpt>节点作为终点
        3. 返回包含起点和终点的列表
    
    注意:
        - 当轨迹点数为0时返回[[0.0,0.0],[0.0,0.0]]
        - 当只有1个轨迹点时起点和终点坐标相同
"""
def find_start_end_location(gpx):
    location=[[0.0,0.0],[0.0,0.0]]
    flag = 0
    for i in range(len(gpx)):
        if gpx[i].count('<trkpt'):
            s_list=gpx[i].split('"')
            lat=Decimal(s_list[1])
            lon=Decimal(s_list[3])
            if flag == 0:
                location[0]=[lat,lon]
                flag = 1
            location[1]=[lat,lon]

    return location

"""
    批量修改轨迹点坐标
    
    参数:
        gpx (list): 原始GPX数据列表，每个元素为字符串行
        lat_add (float): 纬度增量（十进制度数）
        lon_add (float): 经度增量（十进制度数）
        
    返回:
        list: 修改后的GPX数据列表
        
    功能说明:
        1. 对每个<trkpt>节点的经纬度进行加减运算
        2. 自动处理坐标越界：
           - 纬度超过±90度时反向并调整经度
           - 经度超过±180度时进行360度循环
"""
def change_location(gpx,lat_add,lon_add):
    gpx_list=[]
    s=''
    lat_add=Decimal(str(lat_add))
    lon_add=Decimal(str(lon_add))
    for i in range(len(gpx)):
        s=gpx[i]
        if gpx[i].count('<trkpt'):
            s_list=gpx[i].split('"')
            lat=Decimal(s_list[1])+lat_add
            lon=Decimal(s_list[3])+lon_add

            if lat>90:
                lon=lon+180
                lat = 180 - lat_add - s_list[1]
            if lat<-90:
                lon=lon+180
                lat = -(s_list[1] + 180 + lat_add)

            if lon>180:
                lon=lon-360
            if lon<-180:
                lon=lon+360

            s=s_list[0]+'"'+str(lat)+'"'+s_list[2]+'"'+str(lon)+'"'+s_list[4]
        gpx_list.append(s)
    return gpx_list

"""
    随机偏移轨迹点坐标
    
    参数:
        gpx (list): 原始GPX数据列表
        
    返回:
        list: 包含随机偏移后坐标的新GPX数据列表
        
    说明:
        使用data_choice_2中的随机小数进行坐标偏移
"""
def change_location_random(gpx):
    gpx_list=[]
    s=''
    data_choice=[]
    for i in data_choice_2:
        i=Decimal(str(i))
        data_choice.append(i)

    for i in range(len(gpx)):
        s=gpx[i]
        if gpx[i].count('<trkpt'):
            s_list=gpx[i].split('"')
            lat=Decimal(s_list[1])+random.choice(data_choice) 
            lon=Decimal(s_list[3])+random.choice(data_choice) 
            s=s_list[0]+'"'+str(lat)+'"'+s_list[2]+'"'+str(lon)+'"'+s_list[4]
        gpx_list.append(s)
    return gpx_list

"""
    批量修改海拔高度
    
    参数:
        gpx (list): 原始GPX数据列表
        coefficient (int): 海拔系数（乘法因子）
        add (int): 海拔增加值
        
    返回:
        list: 修改后的GPX数据列表
"""
def change_ele(gpx,coefficient,add):
    gpx_list=[]
    s=''
    for i in range(len(gpx)):
        s=gpx[i]
        try:            
            el=str((gpx[i].split("<ele>")[1]).split("</ele>")[0])            
            change_el=int(int(float(el))*coefficient+add)
            s=str(gpx[i].replace(el,str(change_el)))            
        except:
            pass
        gpx_list.append(s)
    return gpx_list

"""
    生成渐进式爬升海拔数据
    
    参数:
        gpx (list): 原始GPX数据列表
        coefficient (int): 海拔递增系数
        add (int): 初始海拔值
        
    返回:
        list: 包含渐进爬升海拔的新GPX数据列表
"""
def change_ele_climb(gpx,coefficient,add):
    gpx_list=[]
    s=''
    j=0
    for i in range(len(gpx)):
        s=gpx[i]
        try:
            el=str((gpx[i].split("<ele>")[1]).split("</ele>")[0])
            change_el=int(j*coefficient+add)
            j=j+1
            s=str(gpx[i].replace(el,str(change_el)))
        except:
            pass
        gpx_list.append(s)
    return gpx_list

"""
    生成测试用数据序列
    
    参数:
        n (int): 数据点数量
        avg (int): 平均值基准
        ran (int): 随机波动范围
        
    返回:
        list: 生成的数据序列，在avg±ran范围内波动
"""
def data_list_creat(n,avg,ran,coefficient=1):
    data_list=[]
    data_list.append(avg)
    data_choice=data_choice_1.copy()
    min_data=avg-ran
    max_data=avg+ran
    for i in range(n-1):  
        num=data_list[i]+(random.choice(data_choice))*coefficient
        if num < min_data:
            num=data_list[i]+1
        if num > max_data:
            num=data_list[i]-1
        data_list.append(num)            
        
    return data_list

"""
    清理指定关键字的扩展数据
    
    参数:
        gpx (list): 原始GPX数据列表
        keywords (str): 要清理的数据关键字（如'extensions'）
        
    返回:
        list: 清理后的GPX数据列表
"""
def clean_data(gpx,keywords):
    gpx_list=[]
    s=''
    clean_flag=0
    for i in range(len(gpx)):
        s=gpx[i]   
        if clean_flag==0:
            if gpx[i].count("<"+keywords+">"):
                clean_flag=1
                s_list=gpx[i].split("<"+keywords+">")
                if gpx[i].count("</"+keywords+">"):
                    clean_flag=0                    
                    s2_list = s_list[1].split("</"+keywords+">")
                    s_list[1]=s2_list[1]
                s=s_list[0]+s_list[1]
        else:
            if gpx[i].count("</"+keywords+">"):
                clean_flag=0
                s_list=gpx[i].split("</"+keywords+">")
                s=s_list[1]
            else:
                s=''            
        gpx_list.append(s)
    return gpx_list

"""
    生成时间序列数据
    
    参数:
        gpx (list): 原始GPX数据列表
        coefficient (int): 时间间隔系数
        time_str (str): 起始时间（ISO 8601格式）
        
    返回:
        list: 包含规范时间序列的新GPX数据
"""
def make_time(gpx,coefficient,time_str):
    gpx_list=[]
    s=''
    first_time=time.mktime(time.strptime(time_str, "%Y-%m-%dT%H:%M:%SZ"))
    now_time=first_time
    flag=0
    for i in range(len(gpx)):
        s=gpx[i]
        if gpx[i].count('</time>') and flag==0:
            s='<time>'+time_str+'</time>\n'
            flag=1
        elif gpx[i].count('<trk>') and flag==0:
            s='<time>'+time_str+'</time>\n'+ gpx[i]
            flag=1
        elif flag==1:
            try:
                if gpx[i].count('</ele>'):
                    now_time=now_time+1*coefficient
                    change_time=time.strftime("%Y-%m-%dT%H:%M:%SZ",time.localtime(now_time))  
                    s_list=s.split("</ele>")
                    s=s_list[0]+'</ele>\n<time>'+change_time+'</time>'+s_list[1]
            except:
                pass
        gpx_list.append(s)
    return gpx_list

"""
    生成指定类型的模拟运动随机数据
    
    参数:
        gpx (list): 原始GPX数据列表
        avg (int): 数据基准平均值
        ran (int): 随机波动范围
        keywords (str): 数据类型（如'heartrate','cadence'）
        
    返回:
        list: 包含模拟数据的新GPX数据
        
    示例:
        >>> 生成平均180bpm，波动±20的心率数据
        make_data_random(gpx, 180, 20, 'heartrate')
"""
def make_data_random(gpx,avg,ran,keywords,coefficient=1):
    gpx_list=[]
    data_list=[]
    num_list=[]
    s=''
    j=0
    distance=0
    for i in range(len(gpx)):
        try:
            if gpx[i].count('</time>'):
                j=j+1
                num_list.append(i)
        except:
            pass

    data_list=data_list_creat(j,avg,ran,coefficient)
    j=0    
    for i in range(len(gpx)):
        s=gpx[i]   
        try:
            if i==num_list[j]: 
                s_list=s.split("</time>")                
                if keywords == "distance":
                    distance=distance+data_list[j]
                    s=s_list[0]+"</time>\n<extensions><"+keywords+">"+str(distance)+"</"+keywords+"></extensions>"+s_list[1]
                else:
                    s=s_list[0]+"</time>\n<extensions><"+keywords+">"+str(data_list[j])+"</"+keywords+"></extensions>"+s_list[1]
                j=j+1
        except:
            pass
        gpx_list.append(s)
    return gpx_list

"""
    生成指定类型的模拟运动随机数据
    
    参数:
        gpx (list): 原始GPX数据列表
        data_list (list): 数据列表
        keywords (str): 数据类型（如'heartrate','cadence'）
        
    返回:
        list: 包含模拟数据的新GPX数据
        
    示例:
        make_data_list(gpx, list, 'heartrate')
"""
def make_data_list(gpx,data_list,keywords):
    gpx_list=[]
    num_list=[]
    s=''
    j=0

    for i in range(len(gpx)):
        try:
            if gpx[i].count('</time>'):
                j=j+1
                num_list.append(i)
        except:
            pass

    j=0    
    data=0

    for i in range(len(gpx)):
        s=gpx[i]   
        try:
            if i==num_list[j]: 
                try:
                    data=data_list[j]                                
                except:
                    data=data

                try:
                    float(data)        
                except:
                    data = 0

                if not data:
                    data=0

                s_list=s.split("</time>")
                s=s_list[0]+"</time>\n<extensions><"+keywords+">"+str(data)+"</"+keywords+"></extensions>"+s_list[1]
                j=j+1
        except:
            pass
        gpx_list.append(s)
    return gpx_list

"""
    按系数调整指定类型的数据值
    
    参数:
        gpx (list): 原始GPX数据列表
        coefficient (float): 调整系数（如0.8表示打八折）
        keywords (str): 要修改的数据类型
        
    返回:
        list: 调整后的GPX数据列表
        
    典型应用:
        功率打折、心率平滑等场景
"""
def change_data(gpx,coefficient,keywords):
    gpx_list=[]
    s=''
    for i in range(len(gpx)):
        s=gpx[i]
        try:
            heart=str((gpx[i].split("<"+keywords+">")[1]).split("</"+keywords+">")[0])
            change=int(int(heart)*coefficient)                
            s=str(gpx[i].replace(heart,str(change)))
        except:
            pass
        gpx_list.append(s)
    return gpx_list

"""
    调整时间轴并添加时间偏移
    
    参数:
        gpx (list): 原始GPX数据列表
        coefficient (float): 时间缩放系数（1.0保持原速）
        time_add (int): 时间偏移量（秒）
        
    返回:
        list: 调整时间后的新GPX数据
        
    算法说明:
        1. 保持首个时间点不变
        2. 后续时间点按系数缩放后偏移
        3. 自动处理跨日时间戳
"""
def change_time(gpx,coefficient,time_add):
    gpx_list=[]
    s=''
    first_time=0
    for i in range(len(gpx)):
        s=gpx[i]
        try:
            ti=str((gpx[i].split("<time>")[1]).split("</time>")[0])
            now_time = time.mktime(time.strptime(ti, "%Y-%m-%dT%H:%M:%SZ"))
            if first_time==0:
                first_time=now_time
                change_time=now_time+time_add
            else:
                change_time=((now_time-first_time)*coefficient)+first_time+time_add
            change_time=time.strftime("%Y-%m-%dT%H:%M:%SZ",time.localtime(change_time))  
            s=str(gpx[i].replace(ti,str(change_time)))
        except:
            pass
        gpx_list.append(s)
    return gpx_list

"""
    复制轨迹段并生成连续轨迹
    
    参数:
        gpx (list): 原始GPX数据列表
        n (int): 复制次数
        random_flag (int): 是否启用随机偏移（1启用/0禁用）
        
    返回:
        list: 包含重复轨迹段的新GPX文件
        
    说明:
        自动处理时间连续性和坐标偏移问题
"""
def copy_gpx(gpx,n,random_flag):
    start_end_location=find_start_end_location(gpx)
    start_lat = float(start_end_location[0][0])
    start_lon = float(start_end_location[0][1])
    end_lat = float(start_end_location[1][0]) 
    end_lon = float(start_end_location[1][1])
    shift_time = 0

    delta_lat = (end_lat - start_lat) * 111
    delta_lon = (end_lon - start_lon) * 111 * math.cos(math.radians(end_lat))
    distance_km = math.sqrt(delta_lat**2 + delta_lon**2)
    # 计算延迟时间（每公里2分钟→120秒）
    shift_time = int(distance_km * 120)

    gpx_list=[]
    gpx_trkpt_list=[]
    gpx_trkpt_list_change=[]
    s=''
    
    first_time=0
    last_time=0
    single_time=0
    
    flag=0
    for i in range(len(gpx)):
        s=gpx[i]
        try:
            ti=str((gpx[i].split("<time>")[1]).split("</time>")[0])
            now_time = time.mktime(time.strptime(ti, "%Y-%m-%dT%H:%M:%SZ"))
            if first_time==0:
                first_time=now_time
            else:
                last_time=now_time
        except:
            pass
        if flag==0:
            gpx_list.append(s)
            if gpx[i].count('<trkseg>'):
                flag=1
        else:
            if gpx[i].count('</trkseg>'):
                s=gpx[i].split("</trkseg>")[0]
                gpx_trkpt_list.append(s)
                break
            else:
                gpx_trkpt_list.append(s)
    
    single_time=last_time-first_time+shift_time
    
    for i in range(n):
        gpx_trkpt_list_change=change_time(gpx_trkpt_list,1,single_time*i+1)

        if random_flag==1:
            gpx_trkpt_list_change=change_location_random(gpx_trkpt_list_change)

        gpx_list=gpx_list+gpx_trkpt_list_change
    gpx_list.append("\n</trkseg>\n</trk>\n</gpx>\n")
    
    return gpx_list

"""
    拼接轨迹段并生成连续轨迹
    
    参数:
        gpx1/gpx2: 原始GPX数据列表
        
    返回:
        list: 包含重复轨迹段的新GPX文件
        
    说明:
        自动处理时间连续性和坐标偏移问题
"""
def splice_gpx(gpx1,gpx2,shift_flag=1,min_per_km=2,shift_time=0):
    if shift_flag:
        start_end_location1=find_start_end_location(gpx1)
        end_lat1 = float(start_end_location1[1][0]) 
        end_lon1 = float(start_end_location1[1][1])

        start_end_location2=find_start_end_location(gpx2)
        start_lat2 = float(start_end_location2[0][0])
        start_lon2 = float(start_end_location2[0][1])

        shift_time = 0

        delta_lat = (start_lat2 - end_lat1) * 111
        delta_lon = (start_lon2 - end_lon1) * 111 * math.cos(math.radians(start_lat2))

        distance_km = math.sqrt(delta_lat**2 + delta_lon**2)
        # 计算延迟时间（每公里2分钟→120秒）
        shift_time = int(distance_km * min_per_km * 60)
        single_time=0
    else:
        single_time=shift_time

    gpx_list=[]
    gpx_list1=[]
    gpx_list2=[]
    gpx_trkpt_list1=[]
    gpx_trkpt_list2=[]

    s=''
    first_time1=0
    last_time1=0   
    flag=0
    for i in range(len(gpx1)):
        s=gpx1[i]
        try:
            ti=str((gpx1[i].split("<time>")[1]).split("</time>")[0])
            now_time = time.mktime(time.strptime(ti, "%Y-%m-%dT%H:%M:%SZ"))
            if first_time1==0:
                first_time1=now_time
            else:
                last_time1=now_time
        except:
            pass
        if flag==0:
            gpx_list1.append(s)
            if gpx1[i].count('<trkseg>'):
                flag=1
        else:
            if gpx1[i].count('</trkseg>'):
                s=gpx1[i].split("</trkseg>")[0]
                gpx_trkpt_list1.append(s)
                break
            else:
                gpx_trkpt_list1.append(s)
    
    first_time2=0
    last_time2=0
    s=''
    flag=0
    for i in range(len(gpx2)):
        s=gpx2[i]
        try:
            ti=str((gpx2[i].split("<time>")[1]).split("</time>")[0])
            now_time = time.mktime(time.strptime(ti, "%Y-%m-%dT%H:%M:%SZ"))
            if first_time2==0:
                first_time2=now_time
            else:
                last_time2=now_time
        except:
            pass
        if flag==0:
            gpx_list2.append(s)
            if gpx2[i].count('<trkseg>'):
                flag=1
        else:
            if gpx2[i].count('</trkseg>'):
                s=gpx2[i].split("</trkseg>")[0]
                gpx_trkpt_list2.append(s)
                break
            else:
                gpx_trkpt_list2.append(s)

    if shift_flag:
        single_time=last_time1 - first_time2 +shift_time
    else:
        single_time=shift_time

    gpx_trkpt_list2_change=change_time(gpx_trkpt_list2,1,single_time+1)

    gpx_list=gpx_list1+gpx_trkpt_list1+gpx_trkpt_list2_change
    gpx_list.append("\n</trkseg>\n</trk>\n</gpx>\n")
    
    return gpx_list

# 更改gpx类型
def change_gpx_flag(gpx,gpx_flag):
    gpx_list = []
    flag = 0
    for i in gpx:        
        if "<metadata>" in i:
            flag=1
        if flag:
            gpx_list.append(i)
    
    gpx_list[0] = gpx_flag + gpx_list[0].split("<metadata>")[-1]

    return gpx_list

# 保存文件
def save_lines(lines,path):
    try:
        f=open(path, 'w', encoding="utf-8")
    except:
        f=open(path, 'a', encoding="utf-8")
    for i in lines:
        f.write(i)
    f.close()
    
'''
读取GPX文件并解析为结构化数据字典

参数:
    gpx (list): GPX文件内容列表，每行字符串为一个元素 需要读取gpx文件后 导入readlines

返回:
    dict: 包含以下键值的结构化数据字典：
        num (list): 数据点索引列表
        time (list): ISO 8601时间戳列表
        lat (list): 纬度值列表（浮点数）
        lon (list): 经度值列表（浮点数）
        ele (list): 海拔高度列表（浮点数）
        key (dict): 扩展数据字典，包含心率/踏频等指标列表

说明:
    1. 自动跳过无法解析的数据行
    2. 时间戳格式为YYYY-MM-DDTHH:MM:SSZ
    3. 无效数据点用None填充
'''
def read_gpx(gpx):
    first_time=0
    first_str_time=""
    trkpt_flag=0
    j=0
    
    num_list=[0]
    time_list=[None]
    ele_list=[None]
    lat_list=[None]
    lon_list=[None]
    key_list=[]
    
    gpx_dict={}
    key_dict={}
    
    for i in keywords_list:
        key_list.append([None])
        
    for i in range(len(gpx)):        
        try:
            ti=str((gpx[i].split("<time>")[1]).split("</time>")[0])
            now_time = time.mktime(time.strptime(ti, "%Y-%m-%dT%H:%M:%SZ"))
            first_time=now_time                
            first_str_time=time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(first_time))  
            break
        except:
            pass
    time_list[0]=first_str_time
    
    for i in range(len(gpx)):
        if gpx[i].count('<trkpt'):
            trkpt_flag=1      
            j=j+1
            num_list.append(j)
            time_list.append(j)
            ele_list.append(j)
            lat_list.append(j)
            lon_list.append(j)            
            for k in range(len(keywords_list)):
                key_list[k].append(0)

        if trkpt_flag==1:
            try:
                ti=str((gpx[i].split("<time>")[1]).split("</time>")[0])
                now_time = time.mktime(time.strptime(ti, "%Y-%m-%dT%H:%M:%SZ"))
                now_str_time=time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(now_time))  
                time_list[j]=now_str_time
            except:
                pass
            
            if gpx[i].count('<trkpt'):
                lat_list[j]=float(gpx[i].split('"')[1])
                lon_list[j]=float(gpx[i].split('"')[3])
                
            try:
                ele_list[j]=float(str((gpx[i].split("<ele>")[1]).split("</ele>")[0]))
            except:
                pass
            
            for k in range(len(keywords_list)):           
                try:
                    dat=str((gpx[i].split("<"+keywords_list[k]+">")[1]).split("</"+keywords_list[k]+">")[0])
                    key_list[k][j]=float(dat)
                except:
                    pass

        if gpx[i].count('</trkpt>'):
            trkpt_flag=0
        
        if gpx[i].count('</trkseg>'):
            break
        
    gpx_dict["num"]=num_list    
    gpx_dict["time"]=time_list       
    gpx_dict["lat"]=lat_list
    gpx_dict["lon"]=lon_list
    gpx_dict["ele"]=ele_list 
    for k in range(len(keywords_list)):
        key_dict[keywords_list[k]]=key_list[k]
    
    gpx_dict["key"]=key_dict

    return gpx_dict

# 格式化相对时间为xx时xx分xx秒
def format_relative_time(seconds,timestamp_flag=0):
    hours = int(seconds // 3600)
    minutes = int((seconds % 3600) // 60)
    secs = int(seconds % 60)
    if timestamp_flag:
        return f"{int(seconds)}"
    else:
        return f"{hours:02d}:{minutes:02d}:{secs:02d}"
        
#显示轨迹图 先读取成gpx_dict然后再显示
def show_gpx_dict(gpx_dict):
    if len(gpx_dict["num"])==0:
        print("no valid time")
        return None
    
    # 创建清理后的数据副本
    cleaned_dict = {
        "num": [],
        "time": [],
        "lat": [],
        "lon": [],
        "ele": [],
        "key": {key: [] for key in gpx_dict["key"].keys()}
    }
    
    for i in range(len(gpx_dict["num"])):
        cleaned_dict["num"].append(gpx_dict["num"][i])
        cleaned_dict["time"].append(gpx_dict["time"][i])
        cleaned_dict["lat"].append(gpx_dict["lat"][i])
        cleaned_dict["lon"].append(gpx_dict["lon"][i])
        cleaned_dict["ele"].append(gpx_dict["ele"][i])
        
        for key in gpx_dict["key"].keys():
            if gpx_dict["key"][key] and i < len(gpx_dict["key"][key]):
                cleaned_dict["key"][key].append(gpx_dict["key"][key][i])
            else:
                cleaned_dict["key"][key].append(None)
    
    # 创建两个子图
    fig, (ax1, ax2) = plot.subplots(1, 2, figsize=(15, 6))
    
    # 第一个图：轨迹图
    lat_list = cleaned_dict["lat"]
    lon_list = cleaned_dict["lon"]
    
    # 删除手动计算经纬度范围的代码，让Matplotlib自动处理None值
    ax1.plot(lon_list, lat_list, 'b-', linewidth=2)
    ax1.scatter(lon_list, lat_list, c='red', s=10, alpha=0.7)

    # 新增：计算实际地理距离比例并设置坐标轴范围
    valid_lat = [lat for lat in lat_list if lat is not None]
    valid_lon = [lon for lon in lon_list if lon is not None]

    if valid_lat and valid_lon:
        # 计算经纬度范围
        lat_min, lat_max = min(valid_lat), max(valid_lat)
        lon_min, lon_max = min(valid_lon), max(valid_lon)
        
        # 计算实际地理距离（米）
        # 纬度距离：1度 ≈ 111公里
        lat_distance = (lat_max - lat_min) * 111000  # 转换为米
        # 经度距离：1度 ≈ 111公里 × cos(纬度)
        avg_lat = (lat_min + lat_max) / 2
        lon_distance = (lon_max - lon_min) * 111000 * math.cos(math.radians(avg_lat))
        
        # 按照1:1的实际距离比例设置坐标轴范围
        if lat_distance > 0 and lon_distance > 0:
            # 计算缩放比例
            if lat_distance > lon_distance:
                # 纬度范围更大，优先显示完整的纬度范围
                lat_range = lat_max - lat_min
                # 同比缩放经度范围
                lon_range_ratio = lon_distance / lat_distance
                lon_range = (lon_max - lon_min) / lon_range_ratio
                lon_center = (lon_min + lon_max) / 2
                lon_min_new = lon_center - lon_range / 2
                lon_max_new = lon_center + lon_range / 2
                
                # 设置坐标轴范围
                ax1.set_xlim(lon_min_new - 0.05*(lon_max_new - lon_min_new), lon_max_new + 0.05*(lon_max_new - lon_min_new))
                ax1.set_ylim(lat_min - 0.05*(lat_max-lat_min), lat_max + 0.05*(lat_max-lat_min))
            else:
                # 经度范围更大，优先显示完整的经度范围
                lon_range = lon_max - lon_min
                # 同比缩放纬度范围
                lat_range_ratio = lat_distance / lon_distance
                lat_range = (lat_max - lat_min) / lat_range_ratio
                lat_center = (lat_min + lat_max) / 2
                lat_min_new = lat_center - lat_range / 2
                lat_max_new = lat_center + lat_range / 2
                
                # 设置坐标轴范围
                ax1.set_xlim(lon_min - 0.05*(lon_max - lon_min), lon_max + 0.05*(lon_max - lon_min))
                ax1.set_ylim(lat_min_new - 0.05*(lat_max_new - lat_min_new), lat_max_new + 0.05*(lat_max_new - lat_min_new))
        else:
            # 如果某个方向距离为0，使用默认的自动范围
            ax1.set_xlim(lon_min - 0.05*(lon_max - lon_min), lon_max + 0.05*(lon_max - lon_min))
            ax1.set_ylim(lat_min - 0.05*(lat_max - lat_min), lat_max + 0.05*(lat_max - lat_min))
    else:
        # 如果没有有效数据，使用自动范围
        pass

    # 标记起始和终止坐标点
    # 找到第一个非空的起始点
    start_index = 0
    while start_index < len(lon_list) and (lon_list[start_index] is None or lat_list[start_index] is None):
        start_index += 1
    
    # 找到最后一个非空的终止点
    end_index = len(lon_list) - 1
    while end_index >= 0 and (lon_list[end_index] is None or lat_list[end_index] is None):
        end_index -= 1
    
    # 如果找到了有效的起始点和终止点
    if start_index < len(lon_list) and end_index >= 0:
        # 起始点（绿色）
        start_lon, start_lat = lon_list[start_index], lat_list[start_index]
        start_location_time = cleaned_dict["time"][start_index]
        ax1.scatter(start_lon, start_lat, c='green', s=100, marker='s', edgecolors='black', linewidth=2, label='Start')

        # 在起始点旁边添加坐标和时间标注
        ax1.annotate(f'Start\n({start_lon:.6f}, {start_lat:.6f})\n{start_location_time}',
                    xy=(start_lon, start_lat),
                    xytext=(10, 10),
                    textcoords='offset points',
                    fontsize=8,
                    bbox=dict(boxstyle='round,pad=0.3', facecolor='white', alpha=0.8))
        
        # 终止点（红色）
        end_lon, end_lat = lon_list[end_index], lat_list[end_index]
        end_location_time = cleaned_dict["time"][end_index]
        ax1.scatter(end_lon, end_lat, c='red', s=100, marker='s', edgecolors='black', linewidth=2, label='End')

        # 在终止点旁边添加坐标和时间标注
        ax1.annotate(f'End\n({end_lon:.6f}, {end_lat:.6f})\n{end_location_time}',
                    xy=(end_lon, end_lat),
                    xytext=(10, 10),
                    textcoords='offset points',
                    fontsize=8,
                    bbox=dict(boxstyle='round,pad=0.3', facecolor='white', alpha=0.8))

    
    # 删除 set_xlim 和 set_ylim 调用
    ax1.set_xlabel('lon')
    ax1.set_ylabel('lat')
    ax1.set_title('Track Points:' + str(cleaned_dict["num"][-1]))
    ax1.grid(True)
    
    # 禁用科学计数法显示
    ax1.ticklabel_format(useOffset=False, style='plain')
    # 添加图例显示起始点和终止点
    ax1.legend(loc='best')

    # 第二个图：变量时间序列图    
    num_list = cleaned_dict["num"]  # 获取序号列表
    time_list = cleaned_dict["time"]
    ele_list = cleaned_dict["ele"]
    key_dict = cleaned_dict["key"]
    
    # 转换时间为相对时间（秒）
    try:
        start_time = datetime.strptime(time_list[0], "%Y-%m-%d %H:%M:%S")
        relative_times = []
        for t in time_list:
            current_time = datetime.strptime(t, "%Y-%m-%d %H:%M:%S")
            delta = current_time - start_time
            relative_times.append(delta.total_seconds())
    except:
        # 如果time为None或格式错误，使用从0开始的正整数递增
        relative_times = list(range(len(time_list)))
        print("time format error")
    
    # 绘制海拔数据 - 使用右侧y轴
    ax2_right1 = ax2.twinx()  # 创建第一个右侧y轴
    ax2_right1.plot(relative_times, ele_list, 'g-', label='ele', linewidth=2)
    
    # 绘制速度数据 - 使用第二个右侧y轴
    ax2_right2 = ax2.twinx()  # 创建第二个右侧y轴
    ax2_right2.spines['right'].set_position(('outward', 60))  # 偏移第二个右侧y轴
    
    # 绘制其他变量数据 - 使用左侧y轴
    colors = ['r', 'b', 'm', 'c', 'y', 'orange']
    color_index = 0
    
    # 定义要显示的变量分组
    left_vars = ['heartrate', 'cadence', 'power']  # 左侧变量
    right_vars = ['speed']  # 右侧变量
    
    # 绘制左侧变量（心率、踏频、功率）
    for key in left_vars:
        if key in key_dict and key_dict[key] and any(v is not None for v in key_dict[key]):
            ax2.plot(relative_times, key_dict[key], colors[color_index % len(colors)] + '-', 
                    label=key, linewidth=1, alpha=0.8)
            color_index += 1
    
    # 绘制右侧速度变量
    if 'speed' in key_dict and key_dict['speed'] and any(v is not None for v in key_dict['speed']):
        ax2_right2.plot(relative_times, key_dict['speed'], colors[color_index % len(colors)] + '-', 
                       label='speed', linewidth=1, alpha=0.8)
        color_index += 1
    
    # 分别设置各组坐标轴范围
    # 左侧变量范围（心率、踏频、功率）
    left_data = []
    for key in left_vars:
        if key in key_dict and key_dict[key]:
            left_data.extend([v for v in key_dict[key] if v is not None])
    
    if left_data:
        left_min = min(left_data)
        left_max = max(left_data)
        # 检查最小值和最大值是否相同，避免设置相同的y轴范围
        if left_min == left_max:
            # 如果所有左侧变量值都相同，设置一个合理的范围
            ax2.set_ylim(left_min - 1, left_max + 1)
        else:
            left_range = left_max - left_min
            ax2.set_ylim(left_min - 0.1 * left_range, left_max + 0.1 * left_range)
    
    # 右侧海拔范围
    ele_data = [v for v in ele_list if v is not None]
    if ele_data:
        ele_min = min(ele_data)
        ele_max = max(ele_data)
        ele_range = ele_max - ele_min
        if ele_min == ele_max:
            # 如果所有海拔值都相同，设置一个合理的范围
            ax2_right1.set_ylim(ele_min - 1, ele_max + 1)
        else:
            ele_range = ele_max - ele_min
            ax2_right1.set_ylim(ele_min - 0.1 * ele_range, ele_max + 0.1 * ele_range)
    
    # 右侧速度范围
    if 'speed' in key_dict and key_dict['speed']:
        speed_data = [v for v in key_dict['speed'] if v is not None]
        if speed_data:
            speed_min = min(speed_data)
            speed_max = max(speed_data)
            # 检查最小值和最大值是否相同，避免设置相同的y轴范围
            if speed_min == speed_max:
                # 如果所有速度值都相同，设置一个合理的范围
                ax2_right2.set_ylim(speed_min - 1, speed_max + 1)
            else:
                speed_range = speed_max - speed_min
                ax2_right2.set_ylim(speed_min - 0.1 * speed_range, speed_max + 0.1 * speed_range)
    
    # 设置坐标轴标签
    ax2.set_xlabel('time')
    ax2.set_ylabel('HR, Cadence, Power')
    ax2_right1.set_ylabel('Elevation (m)', color='g')
    ax2_right2.set_ylabel('Speed (m/s)', color=colors[(color_index-1) % len(colors)])
    
    ax2.set_title('Variable Time Series')
    
    # 合并图例
    lines, labels = ax2.get_legend_handles_labels()
    lines1, labels1 = ax2_right1.get_legend_handles_labels()
    lines2, labels2 = ax2_right2.get_legend_handles_labels()
    ax2.legend(lines + lines1 + lines2, labels + labels1 + labels2, loc='lower right')

    ax2.grid(True)
    
    # 设置x轴范围以匹配实际数据，避免右侧空缺
    if relative_times:
        ax2.set_xlim(min(relative_times), max(relative_times))
    
    # 设置x轴刻度标签为格式化时间 - 修复负值问题
    x_ticks = [tick for tick in ax2.get_xticks() if tick >= 0]
    formatted_labels = [format_relative_time(sec) for sec in x_ticks]
    ax2.set_xticks(x_ticks)
    ax2.set_xticklabels(formatted_labels, rotation=45)
    
    # 在上方添加序号轴
    ax2_secondary = ax2.twiny()  # 创建第二个x轴
    ax2_secondary.set_xlim(ax2.get_xlim())  # 与主x轴范围一致
    
    # 设置序号刻度 - 将时间映射到序号
    if num_list and relative_times:
        # 创建时间到序号的映射
        time_to_num = {rt: num for rt, num in zip(relative_times, num_list)}
        
        # 选择一些关键时间点显示对应的序号
        selected_ticks = []
        selected_labels = []
        
        # 每隔一定时间间隔选择一个刻度
        time_range = max(relative_times) - min(relative_times)
        if time_range > 0:
            num_ticks = min(10, len(num_list))
            tick_interval = time_range / (num_ticks - 1) if num_ticks > 1 else 1
            
            for i in range(num_ticks):
                tick_time = min(relative_times) + i * tick_interval
                # 找到最接近的时间点
                closest_time = min(relative_times, key=lambda x: abs(x - tick_time))
                if closest_time in time_to_num:
                    selected_ticks.append(closest_time)
                    selected_labels.append(str(time_to_num[closest_time]))
        
        ax2_secondary.set_xticks(selected_ticks)
        ax2_secondary.set_xticklabels(selected_labels)
    
    ax2_secondary.set_xlabel('num', labelpad=10)
    ax2_secondary.xaxis.set_label_position('top')  # 标签放在上方
    ax2_secondary.xaxis.tick_top()  # 刻度放在上方

    plot.tight_layout()
    # plot.show()
    return fig

'''
计算轨迹点移动速度
计算的速度与距离 与原本GPX中的distance和speed无关

参数:
    gpx_dict (dict): 包含GPX数据的字典，需包含"time","lat","lon","ele"

    use_ele_flag (bool): 是否考虑海拔高度影响
    use_curvature_flag (bool): 是否考虑曲率影响

内部参数：
    time_list (list): 时间戳列表（ISO 8601格式）
    lat_list (list): 纬度值列表
    lon_list (list): 经度值列表
    ele_list (list): 海拔高度列表
    key_dict (dict): 扩展数据字典，包含心率/踏频等指标列表

返回:
    pandas.DataFrame: 包含以下列的数据框：
        time: 原始时间戳
        lat/lon/ele: 原始坐标数据
        arclength: 相邻点间弧长（米）
        velocity: 计算速度（km/h）

算法说明:
    1. 使用Haversine公式计算地球表面弧长
    2. 考虑海拔高度对地球半径的影响（R = 6371393 + 当前海拔）
    3. 自动处理时间戳相同/乱序的特殊情况
    4. 速度单位转换为km/h（1m/s = 3.6km/h）

注意:
    需安装pandas和numpy库才能正常使用
''' 
def calculate_velocity(gpx_dict,use_ele_flag = True,use_curvature_flag = False):
    time_list = gpx_dict["time"]
    if time_list[0] is None or time_list[0] == "":
        print("time_list[0] None")
        return None, 0, 0, 0
    
    lat_list = gpx_dict["lat"]
    lon_list = gpx_dict["lon"]
    ele_list = gpx_dict["ele"]
    key_dict = gpx_dict["key"]
    
    if len(time_list) != len(lat_list) or len(time_list) != len(lon_list) or len(time_list) != len(ele_list):
        return None, 0, 0, 0
    
    # 使用字典替代DataFrame
    data_dict = {
        'time': time_list,
        'lat': lat_list,
        'lon': lon_list,
        'ele': ele_list,
        'arclength': [None] * len(time_list),
        'velocity': [],
    }
    
    # 添加传感器数据列
    for i, key in enumerate(keywords_list):
        if i < len(key_dict):
            data_dict[key] = key_dict[key]

    if use_curvature_flag:
        # 使用pandas.DataFrame类型来计算曲率
        # data_dict['r'] = 6371393+data_dict['ele'] # radius of the Earth in meters
        
        # data_dict['theta'] = np.deg2rad(data_dict['lon'])
        # data_dict['phi'] = np.deg2rad(data_dict['lat'])
        # data_dict['theta2']=data_dict['theta'].shift()
        # data_dict['phi2']=data_dict['phi'].shift()
        
        # try:
        #     cos_angle = np.cos(data_dict['phi']) * np.cos(data_dict['phi2']) * np.cos(data_dict['theta2']-data_dict['theta']) + np.sin(data_dict['phi'])*np.sin(data_dict['phi2'])
        #     data_dict['arclength'] = data_dict['r'] * np.arccos(np.clip(cos_angle, -1.0, 1.0))
        # except Exception as e:
        #     print(f"计算异常：{e}")
        #     data_dict['arclength'] = data_dict['r'] * np.sqrt((data_dict['theta2']-data_dict['theta'])**2 * np.cos(data_dict['phi'])**2
        #                                         + (data_dict['phi2']-data_dict['phi'])**2
        #                                         )   
        pass
    else:
        # 计算弧长（不使用曲率）
        for i in range(1, len(time_list)):
            try:
                # 提前检查None值
                if (lat_list[i] is None or lat_list[i-1] is None or 
                    lon_list[i] is None or lon_list[i-1] is None):
                    continue
                    
                lat_diff = (lat_list[i] - lat_list[i-1]) * 111000  # 纬度差转米
                lon_diff = (lon_list[i] - lon_list[i-1]) * 111000 * math.cos(math.radians(lat_list[i]))  # 经度差转米
                
                # 检查计算值有效性
                if lat_diff is not None and lon_diff is not None:
                    arclength = math.sqrt(lat_diff**2 + lon_diff**2)
                    
                    if use_ele_flag and ele_list[i] is not None and ele_list[i-1] is not None:
                        ele_diff = ele_list[i] - ele_list[i-1]
                        arclength = math.sqrt(arclength**2 + ele_diff**2)
                    
                    data_dict['arclength'][i] = arclength
            except:
                pass


    elapsed_time_list=[]
    stamp_list=[]
    
    stamp_same_list=[]
    same_flag=0
    enable_flag=0
    exit_same_list=[]
    for i in range(len(time_list)):
        try:
            stamp_list.append(time.mktime(time.strptime(time_list[i], "%Y-%m-%d %H:%M:%S")))
            enable_flag=1
            if i>0 and enable_flag==1:
                if stamp_list[i]>stamp_list[i-1]:
                    elapsed_time_list.append(stamp_list[i]-stamp_list[i-1]) 
                    if same_flag==1:
                        same_flag=0
                        exit_same_list.append(i)
                else:
                    if same_flag==0:
                        stamp_same_list.append(i)
                    same_flag=1
                    elapsed_time_list.append(0.0)
            else:
                elapsed_time_list.append(None)
        except:
            if enable_flag==0:
                elapsed_time_list.append(None)
            else:
                if same_flag==0:
                    stamp_same_list.append(i)
                same_flag=1
                elapsed_time_list.append(0.0)
                
    if len(stamp_same_list)>len(exit_same_list):
        exit_same_list.append(0)  

    j=0
    displacement=0.0
    displacement_time=0.0
    end_displacement=0.0
    same_flag=0
    end_elapsed_time=0.0
    stamp_same_in_1st_flag=0

    # 速度计算（使用字典数据）
    if len(stamp_same_list) == 0:
        for i in range(len(elapsed_time_list)):
            try:
                # 提前检查None值
                if (data_dict['arclength'][i] is None or 
                    data_dict['arclength'][i+1] is None or 
                    elapsed_time_list[i] is None or 
                    elapsed_time_list[i+1] is None):
                    data_dict['velocity'].append(None)
                    continue
                    
                time_diff = elapsed_time_list[i] + elapsed_time_list[i+1]
                if time_diff > 0:
                    numerator = data_dict['arclength'][i] + data_dict['arclength'][i+1]
                    data_dict['velocity'].append(3.6*numerator / time_diff)  # m/s
                else:
                    data_dict['velocity'].append(None)
            except:
                # 检查单个值
                if (data_dict['arclength'][i] is None or 
                    elapsed_time_list[i] is None or 
                    elapsed_time_list[i] <= 0):
                    data_dict['velocity'].append(None)
                else:
                    data_dict['velocity'].append(3.6*data_dict['arclength'][i] / elapsed_time_list[i])  # m/s

    else:  
        for i in range(len(elapsed_time_list)):
            try:
                if i>0:
                    try:
                        if i==stamp_same_list[j]-1:
                            same_flag=1
                                
                        if stamp_same_list[0]==1 and stamp_same_in_1st_flag==0:
                            stamp_same_in_1st_flag=1
                            same_flag=1
                        
                    except:
                        pass
       
                    if same_flag==0:
                        try:
                            time_diff=elapsed_time_list[i]+elapsed_time_list[i+1]
                            if time_diff>0:
                                data_dict['velocity'].append(3.6*(data_dict['arclength'][i]+data_dict['arclength'][i+1])/ (elapsed_time_list[i]+elapsed_time_list[i+1]))  # m/s
                            else:
                                data_dict['velocity'].append(data_dict['velocity'][-1])
                        except:
                            time_diff=elapsed_time_list[i]
                            if time_diff>0:
                                data_dict['velocity'].append(3.6*data_dict['arclength'][i]/ elapsed_time_list[i])  # m/s
                            else:
                                data_dict['velocity'].append(data_dict['velocity'][-1])
                    else:   
                        try:
                            displacement=displacement+data_dict['arclength'][i]
                        except:
                            displacement=displacement+0.0
                        
                        try:
                            displacement_time=displacement_time+elapsed_time_list[i]
                        except:
                            displacement_time=displacement_time+0.0
                        
    
                        if j==len(stamp_same_list)-1 and exit_same_list[j]==0:
                            if end_elapsed_time==0.0:
                                end_elapsed_time=elapsed_time_list[i]                                
                            end_displacement=end_displacement+data_dict['arclength'][i]
                            if end_elapsed_time>0.0:
                                data_dict['velocity'].append(3.6*end_displacement/end_elapsed_time)
                            else:
                                data_dict['velocity'].append(data_dict['velocity'][-1])
                        else:
                            data_dict['velocity'].append(data_dict['velocity'][-1])
    
                        if i==exit_same_list[j]: 
                            if displacement_time>0:
                                avg_velocity=3.6*displacement/displacement_time
                            else:
                                avg_velocity=data_dict['velocity'][-1]
                            for k in range(stamp_same_list[j]-1,i+1):
                                data_dict['velocity'][k]=avg_velocity       
                            j=j+1
                            try:
                                if elapsed_time_list[j]-end_elapsed_time[j-1]<=1:
                                    pass
                                else:
                                    same_flag=0
                                    displacement=0.0
                                    displacement_time=0.0
                            except:
                                same_flag=0
                                displacement=0.0
                                displacement_time=0.0
                        
                else:
                    data_dict['velocity'].append(None)
            except:
                data_dict['velocity'].append(None)

    # 计算总弧长和平均速度
    total_arclength = 0
    for arclength in data_dict['arclength']:
        if arclength is not None and arclength > 0:
            total_arclength += arclength
    
    total_arclength = total_arclength / 1000  # 转公里
    
    # 时间计算
    if len(time_list) > 1:
        try:
            total_first_time = datetime.strptime(time_list[0], "%Y-%m-%d %H:%M:%S")
            total_last_time = datetime.strptime(time_list[-1], "%Y-%m-%d %H:%M:%S")
            final_time_diff = (total_last_time.timestamp() - total_first_time.timestamp()) / 3600
            avg_velocity = total_arclength / final_time_diff if final_time_diff > 0 else 0
        except:
            final_time_diff = 0
            avg_velocity = 0

    # print(data_dict['velocity'])
    return data_dict,total_arclength,final_time_diff,avg_velocity

"""
    将GPX数据转换为SRT字幕格式 直接转 不进行速度计算等
    
    参数:
        gpx (list): 包含GPX原始数据的字符串列表 读取gpx文件后的readlines
        
    返回:
        list: 包含完整SRT字幕内容的列表，每个元素代表一个字幕段落
"""    
def gpx_to_srt(gpx):     
    srt_list=[]
    first_time=0
    srt_time=0
    srt=""
    j=1
    trkpt_flag=0
    trkpt_first_flag=0
    for i in range(len(gpx)):        
        try:
            ti=str((gpx[i].split("<time>")[1]).split("</time>")[0])
            now_time = time.mktime(time.strptime(ti, "%Y-%m-%dT%H:%M:%SZ"))
            first_time=now_time                
            srt_time=0
            str_now_time=time.strftime("<i>%Y-%m-%d %H:%M:%S</i>\n",time.localtime(now_time))  
            srt=str(j)+"\n00:00:00,000 --> 00:00:01,000\n"+str_now_time+"<u>BEGIN</u>\n\n"
            srt_list.append(srt)
            break
        except:
            pass
    
    srt=""
    srt_str_time=""
    last_distance=0
    srt_time=0
    last_srt_time=0
    for i in range(len(gpx)):
        if gpx[i].count('<trkpt'):
            trkpt_flag=1
            trkpt_first_flag=1
            j=j+1

        if trkpt_flag==1:
            try:
                ti=str((gpx[i].split("<time>")[1]).split("</time>")[0])
                now_time = time.mktime(time.strptime(ti, "%Y-%m-%dT%H:%M:%SZ"))                
                srt_time=now_time-first_time
                
                h=int(srt_time/3600)
                m=int(srt_time/60)
                s=int(srt_time%60)
                s2=s+1
                h=trans_time(h)
                m=trans_time(m) 
                s=trans_time(s)
                s2=trans_time(s2)
                srt_str_time=str(h+":"+m+":"+s+",000 --> "+h+":"+m+":"+s2+",000\n")
                str_now_time=time.strftime("<i>%Y-%m-%d %H:%M:%S</i>\n",time.localtime(now_time))  
                srt=srt+str_now_time
            except:
                pass
            
            if gpx[i].count('<trkpt'):
                srt=srt+"lat:"+gpx[i].split('"')[1]+" lon:"+gpx[i].split('"')[3]+"\n"

            try:
                el=str((gpx[i].split("<ele>")[1]).split("</ele>")[0]) 
                srt=srt+"ele:"+el+"\n"
            except:
                pass
            
            for key in keywords_list:                
                try:
                    dat=str((gpx[i].split("<"+key+">")[1]).split("</"+key+">")[0]) 
                    srt=srt+"<i>"+key+":"+dat+"</i>\n"
                    if key=="distance":    
                        velocity_time=srt_time-last_srt_time
                        velocity=(int(dat)-last_distance)/velocity_time*3.6                        
                        srt=srt+"<b>"+"velocity"+":"+str(velocity)+"</b>\n"
                        last_distance=int(dat)
                except:
                    pass
                
            try:
                dat=str((gpx[i].split("<velocity>")[1]).split("</velocity>")[0]) 
                srt=srt+"<b>"+"velocity"+":"+dat+"</b>\n"
            except:
                pass

            try:
                dat=str((gpx[i].split("<arclength>")[1]).split("</arclength>")[0]) 
                srt=srt+"<i>"+"arclength"+":"+dat+"</i>\n"
            except:
                pass
        
        if gpx[i].count('</trkpt>'):
            trkpt_flag=0
        
        if trkpt_first_flag==1 and trkpt_flag==0:
            if not srt=="":                
                last_srt_time=int(srt_time)
                srt=str(j)+"\n"+srt_str_time+srt+"\n"
                srt_list.append(srt)
                srt=""
        
        if gpx[i].count('</trkseg>'):
            j=j+1
            trkpt_first_flag=2
            
        if trkpt_first_flag==2:
            h=int((last_srt_time+1)/3600)
            m=int((last_srt_time+1)/60)
            s=int((last_srt_time+1)%60)
            s2=s+1
            h=trans_time(h)
            m=trans_time(m) 
            s=trans_time(s)
            s2=trans_time(s2)
            srt_str_time=str(h+":"+m+":"+s+",000 --> "+h+":"+m+":"+s2+",000\n")
            srt=str(j)+"\n"+srt_str_time+"<u>END</u>\n"+"\n"
            srt_list.append(srt)
            srt=""
            break
   
    return srt_list

"""
    将包含速度计算的DataFrame转换为SRT字幕格式 需要首先经过速度计算得到DataFrame的GPX数据
    
    参数:
        data_dict (DataFrame): 包含轨迹数据的DataFrame，需包含以下列：
            time: 时间戳（与原函数相同格式）
            lat/lon/ele: 坐标和海拔
            heartrate/cadence/power/velocity: 传感器数据
            
    返回:
        list: 包含完整SRT字幕内容的列表
"""
def gpx_dict_to_srt(data_dict):
    srt_list = []
    if data_dict is None:
        return srt_list
    
    total_arclength = 0.0
    
    # 解析第一个时间点作为基准时间
    first_time_str = data_dict['time'][0]
    try:
        # 尝试解析带时区的时间格式
        first_time_ts = time.mktime(time.strptime(first_time_str, "%Y-%m-%dT%H:%M:%SZ"))
    except:
        try:
            # 尝试解析不带时区的时间格式
            first_time_ts = time.mktime(time.strptime(first_time_str, "%Y-%m-%d %H:%M:%S"))
        except:
            # 如果解析失败，使用索引作为备用方案
            first_time_ts = 0
    
    # 初始化首条字幕
    srt_list.append(f"1\n00:00:00,000 --> 00:00:01,000\n<i>{first_time_str}</i>\n<u>BEGIN</u>\n\n")
    
    # 生成数据点字幕
    for i in range(len(data_dict['time'])):
        # 解析当前时间点
        current_time_str = data_dict['time'][i]
        try:
            # 尝试解析带时区的时间格式
            current_time_ts = time.mktime(time.strptime(current_time_str, "%Y-%m-%dT%H:%M:%SZ"))
        except:
            try:
                # 尝试解析不带时区的时间格式
                current_time_ts = time.mktime(time.strptime(current_time_str, "%Y-%m-%d %H:%M:%S"))
            except:
                # 如果解析失败，使用索引作为备用方案
                current_time_ts = i
        
        # 计算相对于第一个时间点的时间差（秒）
        time_diff = current_time_ts - first_time_ts
        
        # 时间计算 - 基于实际时间差
        start_sec = int(time_diff)
        end_sec = start_sec + 1
        
        # 格式化时间字符串
        h_start = start_sec // 3600
        m_start = (start_sec % 3600) // 60
        s_start = start_sec % 60
        
        h_end = end_sec // 3600
        m_end = (end_sec % 3600) // 60
        s_end = end_sec % 60
        
        time_str = f"{h_start:02}:{m_start:02}:{s_start:02},000 --> {h_end:02}:{m_end:02}:{s_end:02},000\n"
        
        # 构建内容
        lat_str = f"{data_dict['lat'][i]:.7f}" if data_dict['lat'][i] is not None else "N/A"
        lon_str = f"{data_dict['lon'][i]:.7f}" if data_dict['lon'][i] is not None else "N/A"
        ele_str = f"{data_dict['ele'][i]:.1f}" if data_dict['ele'][i] is not None else "N/A"
        
        content = f"<i>{data_dict['time'][i]}</i>\n" \
                 f"lat:{lat_str} lon:{lon_str}\n" \
                 f"ele:{ele_str}\n" \
                 f"<u>total arclength:{total_arclength/1000:.2f}</u>\n"
        
        # 添加传感器数据
        for key in keywords_list:
            if key in data_dict and data_dict[key][i] is not None:
                content += f"<i>{key}:{data_dict[key][i]:.0f}</i>\n"
        
        # 添加速度数据
        if 'velocity' in data_dict and data_dict['velocity'][i] is not None:
            content += f"<b>velocity:{data_dict['velocity'][i]:.1f}</b>\n"
        
        # 总距离计算
        if 'arclength' in data_dict and data_dict['arclength'][i] is not None:
            total_arclength += data_dict['arclength'][i]
        
        # 构建字幕
        srt_list.append(f"{i+2}\n{time_str}{content}\n")
    
    # 添加结束标记 - 修复KeyError: 'num'问题
    last_time_diff = int(time_diff) if 'time_diff' in locals() else len(data_dict)
    end_start_sec = last_time_diff + 1
    end_end_sec = end_start_sec + 1
    
    h_end_start = end_start_sec // 3600
    m_end_start = (end_start_sec % 3600) // 60
    s_end_start = end_start_sec % 60
    
    h_end_end = end_end_sec // 3600
    m_end_end = (end_end_sec % 3600) // 60
    s_end_end = end_end_sec % 60
    
    end_time_str = f"{h_end_start:02}:{m_end_start:02}:{s_end_start:02},000 --> {h_end_end:02}:{m_end_end:02}:{s_end_end:02},000\n"
    srt_list.append(f"{len(data_dict['time'])+2}\n{end_time_str}<u>END</u>\n\n")
    
    return srt_list

def demo():
    pass

    path="C:/Users/ZHOU/Desktop/1.gpx"
    save_path="C:/Users/ZHOU/Desktop/2.gpx"

    f=open(path, 'r', encoding="utf-8")
    gpx=f.readlines()
    f.close()    

    # gpx=change_location(gpx,7.867624,0.011124)
    # #gpx=make_time(gpx,1,"2025-08-27T15:05:04Z")
    # gpx=change_ele(gpx,1,-185)
    # gpx=change_ele_climb(gpx,1,100)
    gpx=make_data_random(gpx,160,20,"heartrate")
    gpx=make_data_random(gpx,250,50,"power")
    # gpx=clean_data(gpx,"extensions")
    # gpx=make_data_random(gpx,90,20,"cadence")
    # gpx=change_data(gpx,1.5,"power")
    # gpx = change_gpx_flag(gpx,COROS_GPX_FLAG)
    # gpx=change_time(gpx,1,-1800*24*8)
    # gpx=change_location_random(gpx)
    # gpx=copy_gpx(gpx,3,1)
    
    gpx_dict=read_gpx(gpx)
    show_gpx_dict(gpx_dict)
    data_dict=calculate_velocity(gpx_dict)[0]
    srt_list=gpx_dict_to_srt(data_dict)

    gpx=make_data_list(gpx,data_dict["velocity"],"velocity")
    save_lines(srt_list,"C:/Users/ZHOU/Desktop/1.srt")
    save_lines(gpx,save_path)

    f=open("C:/Users/ZHOU/Desktop/a.gpx", 'r', encoding="utf-8")
    gpx1=f.readlines()
    f.close()    

    f=open("C:/Users/ZHOU/Desktop/b.gpx", 'r', encoding="utf-8")
    gpx2=f.readlines()
    f.close()    

    gpx=splice_gpx(gpx1,gpx2)
    gpx = change_time(gpx,1,10000)

    gpx = change_gpx_flag(gpx,COROS_GPX_FLAG)
    save_lines(gpx,"C:/Users/ZHOU/Desktop/c.gpx")

# demo()
